/******************************************************************************
This file is part of AppKit.
Project: appkit
Author : FergusZeng
Email  : cblock@126.com
git	   : https://gitee.com/newgolo/appkit.git
*******************************************************************************
MIT License

Copyright (c) 2022 cblock@126.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#include "appkit/fileio.h"

#include <dirent.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/eventfd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <unistd.h>

#include <algorithm>

#include "appkit/datetime.h"
#include "appkit/regex.h"
#include "appkit/strutil.h"
#include "appkit/system.h"
#include "appkit/tracer.h"

namespace appkit {

IODevice::IODevice() {}

IODevice::~IODevice() { close(); }

bool IODevice::open(const std::string& devName, int mode) {
    if (devName.empty() || (mode < 0) || (mode > IO_MODE_APPEND_ONLY)) {
        TRACE_ERR_CLASS("param error!");
        return false;
    }

    if (m_fd >= 0) {
        TRACE_ERR_CLASS("device is already opened!");
        return false;
    }
    // open时使用O_CLOEXEC标志可以防止子进程占用父进程已经打开的文件
    switch (mode) {
        case IO_MODE_RD_ONLY:
            m_fd = ::open(CSTR(devName), O_RDONLY | O_CLOEXEC);
            break;
        case IO_MODE_WR_ONLY:
            m_fd = ::open(CSTR(devName), O_WRONLY | O_CLOEXEC);
            break;
        case IO_MODE_RDWR_ONLY:
            m_fd = ::open(CSTR(devName), O_RDWR | O_CLOEXEC);
            break;
        case IO_MODE_APPEND_ONLY:
            m_fd =
                ::open(CSTR(devName), O_RDWR, O_CREAT | O_APPEND | O_CLOEXEC);
            break;
        default:
            TRACE_ERR_CLASS("Unsupport IO Mode: %d!", mode);
            return false;
    }

    if (m_fd < 0) {
        TRACE_ERR_CLASS("open %s error: %s!", CSTR(devName), ERRSTR);
        return false;
    }
    m_name = devName;
    m_openMode = mode;
    return true;
}

bool IODevice::close() {
    if (m_fd >= 0) {
        ::close(m_fd);
        m_fd = -1;
    }
    return true;
}

int IODevice::readData(char* buf, int len) {
    int rc;
    if (!buf || len <= 0) {
        TRACE_ERR_CLASS("param error!");
        return RC_ERROR;
    }

    if (m_fd < 0) {
        TRACE_ERR_CLASS("device not open.");
        return RC_ERROR;
    }

    rc = ::read(m_fd, buf, len);
    if (rc < 0) {
        if (rc == -1 && errno != EAGAIN) {
            // TRACE_ERR_CLASS("read error:%s", ERRSTR);
        }
        return RC_ERROR;
    }
    return rc;
}

int IODevice::writeData(const char* buf, int len) {
    int rc;
    if (!buf || len <= 0) {
        TRACE_ERR_CLASS("param error.");
        return RC_ERROR;
    }

    if (m_fd < 0) {
        TRACE_ERR_CLASS("device not open.");
        return RC_ERROR;
    }

    rc = ::write(m_fd, buf, len);
    if (rc < 0) {
        if (rc == -1 && errno != EAGAIN) {
            // TRACE_ERR_CLASS("write error:%s", ERRSTR);
        }
        return RC_ERROR;
    }
    return rc;
}

int IODevice::recvData(char* buf, int count, int usTimeout) {
    int rc;
    if (!buf || count <= 0) {
        TRACE_ERR_CLASS("param error.");
        return RC_ERROR;
    }

    if (m_fd < 0) {
        TRACE_ERR_CLASS("device not open.");
        return RC_ERROR;
    }

    struct timeval tv, *ptv;
    fd_set readfds;
    if (usTimeout < 0) {
        ptv = nullptr;
    } else {
        ptv = &tv;
        tv.tv_sec = usTimeout / 1000000;
        tv.tv_usec = usTimeout % 1000000;
    }
    FD_ZERO(&readfds);
    FD_SET(m_fd, &readfds);
    rc = select(m_fd + 1, &readfds, nullptr, nullptr, ptv);
    if (0 == rc) {
        // TRACE_INFO_CLASS("recv timeout.");
        return RC_TIMEOUT;
    } else if ((rc < 0) && (errno != EINTR)) {  // EINTR表示被信号打断
        TRACE_ERR_CLASS("select error:%s", ERRSTR);
        return RC_ERROR;
    }
    /* 检查fd是否可读 */
    if (!FD_ISSET(m_fd, &readfds)) {
        TRACE_ERR_CLASS("readfds error");
        return RC_ERROR;
    }

    rc = ::read(m_fd, buf, count);
    if (rc < 0) {
        if (rc == -1 && errno != EAGAIN) {
            // TRACE_ERR_CLASS("read error:%s", ERRSTR);
        }
        return RC_ERROR;
    }
    return rc;
}

int IODevice::sendData(const char* data, int len, int usTimeout) {
    int rc;
    if ((!data) || len <= 0) {
        TRACE_ERR_CLASS("param error.");
        return RC_ERROR;
    }

    if (m_fd < 0) {
        TRACE_ERR_CLASS("device not open.");
        return RC_ERROR;
    }

    struct timeval tv, *ptv;
    fd_set writefds;
    if (usTimeout < 0) {
        ptv = nullptr;
    } else {
        ptv = &tv;
        tv.tv_sec = usTimeout / 1000000;
        tv.tv_usec = usTimeout % 1000000;
    }
    FD_ZERO(&writefds);
    FD_SET(m_fd, &writefds);
    rc = select(m_fd + 1, nullptr, &writefds, nullptr, ptv);
    if (0 == rc) {
        // TRACE_ERR_CLASS("send timeout.");
        return RC_TIMEOUT;
    } else if (rc < 0) {
        TRACE_ERR_CLASS("select error:%s", ERRSTR);
        return RC_ERROR;
    }

    /* 检查fd是否可写 */
    if (!FD_ISSET(m_fd, &writefds)) {
        TRACE_ERR_CLASS("writefds error");
        return RC_ERROR;
    }

    rc = ::write(m_fd, data, len);
    if (rc < 0) {
        if (rc == -1 && errno != EAGAIN) {
            // TRACE_ERR_CLASS("write error: %s", ERRSTR);
        }
        return RC_ERROR;
    }
    return rc;
}

int IODevice::setAttribute(int attr, int value) { return RC_ERROR; }

int IODevice::getAttribute(int attr) { return RC_ERROR; }

int IODevice::fd() { return m_fd; }

bool IODevice::isOpen() { return (m_fd > 0) ? true : false; }

bool IODevice::reopen() {
    close();
    return open(m_name, m_openMode);
}

void IODevice::flush() {
    if (m_fd > 0) {
        fsync(m_fd);
    }
}

File::File() {}

File::~File() { this->close(); }

bool File::exists(const std::string& filePath) {
    if (0 == access(CSTR(filePath), F_OK)) {
        struct stat fileStat;
        if (0 != lstat(CSTR(filePath), &fileStat)) {
            return false;
        }
        if (S_ISDIR(fileStat.st_mode)) {
            return false;
        } else {
            return true;
        }
    }
    return false;
}

int File::type(const std::string& fileName) {
    struct stat fileStat;
    if (0 != lstat(CSTR(fileName), &fileStat)) {
        return -1;
    } else {
        if (S_ISREG(fileStat.st_mode))
            return File::FileType::REG;
        else if (S_ISDIR(fileStat.st_mode))
            return File::FileType::DIR;
        else if (S_ISCHR(fileStat.st_mode))
            return File::FileType::CHR;
        else if (S_ISBLK(fileStat.st_mode))
            return File::FileType::BLK;
        else if (S_ISFIFO(fileStat.st_mode))
            return File::FileType::FIFO;
        else if (S_ISLNK(fileStat.st_mode))
            return File::FileType::LINK;
        else if (S_ISSOCK(fileStat.st_mode))
            return File::FileType::SOCK;
        else
            return -1;
    }
}

bool File::removeFile(const std::string& fileName) {
    if (remove(CSTR(fileName)) != 0) {
        TRACE_ERR("remove file(%s) error: %s.", CSTR(fileName), ERRSTR);
        return false;
    }
    return true;
}

bool File::renameFile(const std::string& oldFile, const std::string& newFile) {
    if (rename(CSTR(oldFile), CSTR(newFile)) != 0) {
        TRACE_ERR("rename file(%s)->(%s) error: %s.", CSTR(oldFile),
                  CSTR(newFile), ERRSTR);
        return false;
    }
    return true;
}

bool File::truncFile(const std::string& fileName, int size) {
    File file;
    if (size < 0) {
        return false;
    }
    if (!file.open(fileName, IO_MODE_RDWR_ONLY)) {
        TRACE_ERR("open file(%s) error: %s", VSTR(fileName));
        return false;
    }
    if (ftruncate(file.fd(), size) != 0) {
        return false;
    }
    return true;
}

std::string File::readAll(const std::string& fileName) {
    File file;
    if (!file.open(fileName, IO_MODE_RD_ONLY)) {
        TRACE_ERR("open file(%s) error: %s", VSTR(fileName));
        return "";
    }
    return file.readString(-1);
}
int File::getSize(const std::string& fileName) {
    struct stat statbuf;
    if (0 == lstat(CSTR(fileName), &statbuf)) {
        int size = statbuf.st_size;
        return size;
    }
    return 0;
}

bool File::isOpen() {
    if (!m_fp) {
        return false;
    }
    return true;
}

bool File::open(const std::string& fileName, int ioMode) {
    std::string modestr;
    if (fileName.empty()) {
        TRACE_ERR_CLASS("file name is empty.");
        return false;
    }

    switch (ioMode) {
        case IO_MODE_RD_ONLY:
            modestr = "rb";
            break;
        case IO_MODE_WR_ONLY:
            modestr = "wb";
            break;
        case IO_MODE_RDWR_ONLY:
            modestr = "rb+";
            break;
        case IO_MODE_REWR_ORNEW:
            modestr = "wb";
            break;
        case IO_MODE_RDWR_ORNEW:
            modestr = "wb+";
            break;
        case IO_MODE_APPEND_ORNEW:
            modestr = "ab";
            break;
        case IO_MODE_RDAPPEND_ORNEW:
            modestr = "ab+";
            break;
        default:
            TRACE_ERR_CLASS("Unsupport mode(%d)");
            return false;
    }

    if (m_fp) {
        fclose(m_fp);
        m_fp = nullptr;
    }

    m_fp = fopen(CSTR(fileName), CSTR(modestr));
    if (!m_fp) {
        TRACE_ERR_CLASS("open file:%s, error:%s.", CSTR(fileName), ERRSTR);
        return false;
    }
    m_fd = fileno(m_fp);
    m_name = fileName;
    return true;
}

bool File::close() {
    if (m_fp) {
        fclose(m_fp);
        m_fp = nullptr;
    }
    return true;
}

int File::readData(char* buf, int len) {
    if (!m_fp) {
        TRACE_ERR_CLASS("File not open.");
        return RC_ERROR;
    }
    if (!buf || len <= 0) {
        TRACE_ERR_CLASS("parameter error:buf=0x%x,count=%d.", buf, len);
        return RC_ERROR;
    }
    return fread(buf, 1, len, m_fp);
}

int File::writeData(const char* buf, int len) {
    int rc;
    if (!m_fp) {
        TRACE_ERR_CLASS("File not open.");
        return RC_ERROR;
    }
    if (!buf || len <= 0) {
        TRACE_ERR_CLASS("parameter error.");
        return RC_ERROR;
    }
    rc = fwrite(buf, 1, len, m_fp);
    fflush(m_fp);
    return rc;
}

void File::flush() {
    if (m_fp) {
        fflush(m_fp);
    }
}

void* File::mapMemory() {
    if (!m_mmaddr) {
        m_mmaddr = mmap(nullptr, getSize(), PROT_READ | PROT_WRITE, MAP_SHARED,
                        fileno(m_fp), 0);
        if (MAP_FAILED == m_mmaddr) {
            TRACE_ERR_CLASS("file[%s] map memory error:%s!", CSTR(m_name),
                            ERRSTR);
            return nullptr;
        }
    }
    return m_mmaddr;
}

bool File::unmapMemory() {
    if (m_mmaddr) {
        if (-1 == munmap(m_mmaddr, getSize())) {
            TRACE_ERR_CLASS("file[%s] unmap memory error:%s!", CSTR(m_name),
                            ERRSTR);
            return false;
        }
        m_mmaddr = nullptr;
    }
    return true;
}

int File::readLine(std::string* lineStr) {
    int rc;
    if (!m_fp) {
        TRACE_ERR_CLASS("File not open.");
        return RC_ERROR;
    }
    lineStr->clear();
    for (;;) {
        char buf[2] = {0};
        rc = readData(buf, 1);
        if (rc <= 0) {
            return RC_ERROR;
        } else {
            lineStr->append(buf);
            if (std::string::npos != lineStr->find("\r\n")) {
                *lineStr = lineStr->substr(0, lineStr->size() - 2);
                return lineStr->size();
            } else if (std::string::npos != lineStr->find("\n")) {
                *lineStr = lineStr->substr(0, lineStr->size() - 1);
                return lineStr->size();
            }
        }
    }
}

std::string File::readAll() { return File::readAll(m_name); }

std::string File::readString(size_t size) {
    std::string result = "";
    char buf[512] = {0};

    if (size <= 0) {
        size = ~(static_cast<size_t>(0));
    }

    size_t blocks = size / sizeof(buf);
    size_t spares = size % sizeof(buf);
    for (auto n = 0; n < blocks; n++) {
        auto len = readData(buf, sizeof(buf));
        if (len <= 0) {
            return result;
        } else {
            result.append(buf, len);
        }
        if (len != sizeof(buf)) {
            return result;
        }
    }
    if (spares > 0) {
        int len = readData(buf, spares);
        if (len > 0) {
            result.append(buf, len);
        }
    }
    return result;
}

int File::getSize() {
    int fileSize, pos;
    if (!m_fp) {
        TRACE_ERR_CLASS("File not open.");
        return RC_ERROR;
    }
    pos = ftell(m_fp); /* 保存当前读写位置 */
    if (fseek(m_fp, 0, SEEK_END) < 0) {
        return RC_ERROR;
    }
    fileSize = ftell(m_fp);
    if (fileSize < 0) {
        fileSize = RC_ERROR;
    }
    fseek(m_fp, pos, SEEK_SET); /* 恢复当前读写位置 */
    return fileSize;
}

std::string File::getName() { return m_name; }

bool File::isEnd() {
    int pos = getPos();
    if (pos == RC_ERROR || pos != getSize()) {
        return false;
    }
    return true;
}

int File::getPos() {
    int pos;
    if (!m_fp) {
        TRACE_ERR_CLASS("File not open.");
        return RC_ERROR;
    }
    pos = ftell(m_fp);
    if (pos < 0) {
        return RC_ERROR;
    } else {
        return pos;
    }
}

int File::setPos(int pos) {
    if (!m_fp) {
        TRACE_ERR_CLASS("File not open.");
        return RC_ERROR;
    }
    if (0 == fseek(m_fp, pos, SEEK_SET)) {
        return RC_OK;
    } else {
        return RC_ERROR;
    }
}

FilePath::FilePath(const std::string& filePath) : m_filePath(filePath) {}
FilePath::~FilePath() {}
FilePath& FilePath::addPath(const std::string& path) {
    if (m_filePath.back() == '/') {
        m_filePath.append(path);
    } else {
        m_filePath.append("/").append(path);
    }
    return *this;
}

std::string FilePath::dirName() {
    std::string ret = "";
    if (m_filePath.empty()) {
        return ret;
    }
    char* path = strdup(CSTR(m_filePath));
    if (path) {
        ret = std::string(dirname(path));
        free(path);
    }
    return ret;
}

std::string FilePath::baseName() {
    std::string ret = "";
    if (m_filePath.empty()) {
        return ret;
    }
    char* path = strdup(CSTR(m_filePath));
    if (path) {
        ret = std::string(basename(path));
        free(path);
    }
    return ret;
}

std::string FilePath::suffix() { return StrUtil::suffix(baseName(), "."); }

bool FilePath::createLink(const std::string& filePath,
                          const std::string& linkPath, bool hard) {
    if (filePath.empty() || linkPath.empty()) {
        return false;
    }
    if (hard) {
        if (link(CSTR(filePath), CSTR(linkPath)) != 0) {
            TRACE_DBG("create hard link error:%s", ERRSTR);
            return false;
        }
    } else {
        if (symlink(CSTR(filePath), CSTR(linkPath)) != 0) {
            TRACE_DBG("create soft link error:%s", ERRSTR);
            return false;
        }
    }
    return true;
}

Directory::Directory() {}

Directory::~Directory() {}

bool Directory::isEmpty(const std::string& dirName) {
    DIR* dirp = nullptr;
    struct dirent* dp = nullptr;
    int num = 0;
    dirp = opendir(CSTR(dirName));
    if (!dirp) {
        return true;
    }
    while ((dp = readdir(dirp))) {
        if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
            continue;
        } else {
            num++;
            break;
        }
    }
    closedir(dirp);
    if (num > 0) {
        return false;
    }
    return true;
}

bool Directory::exists(const std::string& dirName) {
    if (dirName.empty()) {
        return false;
    }
    if (0 == access(CSTR(dirName), F_OK)) {
        struct stat fileStat;
        if (0 != lstat(CSTR(dirName), &fileStat)) {
            return false;
        } else {
            if (S_ISDIR(fileStat.st_mode)) {
                return true;
            }
            if (S_ISLNK(fileStat.st_mode)) {
                DIR* dirp = opendir(CSTR(dirName));
                if (dirp) {
                    closedir(dirp);
                    return true;
                }
            }
        }
    }
    return false;
}

uint32 Directory::getSize(const std::string& dirName) {
    if (Directory::exists(dirName)) {
        uint32 sum = 0;
        Directory dir;
        std::string path(dirName);
        dir.enterDir(path);
        std::vector<std::string> fileList = dir.getFileList();

        int fileNum = fileList.size();
        for (auto i = 0; i < fileNum; i++) {
            if (fileList[i] == "." || fileList[i] == "..") {
                continue;
            }
            std::string fileName = path + "/";
            fileName.append(fileList[i]);
            if (File::exists(CSTR(fileName))) {
                sum += File::getSize(CSTR(fileName));
            }
            if (Directory::exists(CSTR(fileName))) {
                sum += File::getSize(CSTR(fileName));
                sum += Directory::getSize(CSTR(fileName));
            }
        }
        return sum;
    } else {
        TRACE_ERR("%s not exist!", CSTR(dirName));
        return 0;
    }
}

bool Directory::createDir(const std::string& dirName, int mode,
                          bool recursive) {
    if (dirName.empty()) return false;
    if (recursive) {
        std::vector<std::string> dirNames =
            StrUtil::splitString(dirName, "/", true);
        if (!dirNames.empty()) {
            std::string dirPath = "";
            for (auto i = 0; i < dirNames.size(); i++) {
                if (dirNames[i] == "/") {
                    if (i == 0) dirPath += "/";
                } else {
                    dirPath += dirNames[i];
                    dirPath += "/";
                    if (!Directory::exists(CSTR(dirPath))) {
                        if (mkdir(CSTR(dirPath), mode) != 0) {
                            TRACE_ERR("Directory::createDir(%s) error:%s.",
                                      CSTR(dirPath), ERRSTR);
                            return false;
                        }
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    } else {
        if (mkdir(CSTR(dirName), mode) != 0) {
            TRACE_ERR("Directory::createDir(%s) error:%s.", CSTR(dirName),
                      ERRSTR);
            return false;
        }
        return true;
    }
}

bool Directory::removeDir(const std::string& dirName, bool recursive) {
    if (dirName.empty()) return false;
    if (recursive) {
        std::vector<std::string> dirNames =
            StrUtil::splitString(dirName, "/", true);
        if (!dirNames.empty()) {
            std::string dirPath = "";
            for (auto i = 0; i < dirNames.size(); i++) {
                if (dirNames[i] == "/") {
                    if (i == 0 || (i == (dirNames.size() - 1))) {
                        dirPath += "/";
                    }
                } else {
                    dirPath += dirNames[i];
                    dirPath += "/";
                }
            }
            if (Directory::exists(CSTR(dirPath))) {
                if (!Directory::isEmpty(
                        CSTR(dirPath))) { /* 先删除该目录下的所有子目录和文件 */
                    Directory dir;
                    dir.enterDir(CSTR(dirPath));
                    std::vector<std::string> allFiles = dir.getFileList();
                    for (auto i = 0; i < allFiles.size(); i++) {
                        if (allFiles[i] == "." || allFiles[i] == "..") {
                            continue;
                        } else {
                            std::string fileName = dirPath + allFiles[i];
                            if (Directory::exists(CSTR(fileName))) {
                                if (!removeDir(CSTR(fileName), true)) {
                                    TRACE_ERR(
                                        "Directory::removeDir,del1 dir(%s) "
                                        "error:%s.",
                                        CSTR(fileName), ERRSTR);
                                    return false;
                                }
                            } else {
                                if (!File::removeFile(CSTR(fileName))) {
                                    TRACE_ERR(
                                        "Directory::removeDir,del1 file(%s) "
                                        "error:%s.",
                                        CSTR(fileName), ERRSTR);
                                    return false;
                                }
                            }
                        }
                    }
                }
            }
            return removeDir(CSTR(dirPath)); /* 删除目录 */
        }
        return false;
    } else {
        if (remove(CSTR(dirName)) != 0) {
            TRACE_ERR("Directory::removeDir(%s) error:%s.", CSTR(dirName),
                      ERRSTR);
            return false;
        }
        return true;
    }
}

std::vector<std::string> Directory::getFileList(const std::string& dirName,
                                                const std::string& regexp) {
    std::vector<std::string> fileVector;
    std::string fileName;
    DIR* dirp = opendir(CSTR(dirName));
    if (!dirp) {
        return fileVector;
    }
    struct dirent* dp;
    Regex reg;
    while ((dp = readdir(dirp))) {
        fileName = std::string(dp->d_name);
        if (regexp.empty() || reg.match(regexp, fileName)) {
            fileVector.emplace_back(fileName);
        }
    }
    closedir(dirp);
    if (fileVector.size() > 1) {
        sort(fileVector.begin(), fileVector.end());
    }
    return fileVector;
}

bool Directory::enterDir(const std::string& dirName) {
    std::string resolved;
    if (dirName.empty()) {
        return false;
    }
    if (dirName[0] != '/') {
        resolved = m_currentDir + "/" + dirName;
    } else {
        resolved = dirName;
    }
    DIR* dirp;
    dirp = opendir(CSTR(resolved));
    if (!dirp) {
        return false;
    }
    closedir(dirp);
    char buf[PATH_MAX] = {0};
    if (!realpath(CSTR(resolved), buf)) {
        return false;
    }
    m_currentDir = std::string(buf);
    return true;
}

std::string Directory::currentDir() { return m_currentDir; }

std::vector<std::string> Directory::getFileList() {
    std::vector<std::string> fileVector;
    std::string fileName;
    DIR* dirp = opendir(CSTR(m_currentDir));
    if (!dirp) {
        return fileVector;
    }
    struct dirent* dp;
    while ((dp = readdir(dirp))) {
        fileName = std::string(dp->d_name);
        fileVector.emplace_back(fileName);
    }
    closedir(dirp);
    if (fileVector.size() > 1) {
        sort(fileVector.begin(), fileVector.end());
    }
    return fileVector;
}

bool Partition::statist(const std::string& volume) {
    struct statvfs stat;
    if (statvfs(volume.data(), &stat) != 0) {
        TRACE_ERR_CLASS("stat dir(%s) error: %s", volume.data(), ERRSTR);
        return false;
    }
    m_total = stat.f_frsize * stat.f_blocks;
    m_free = stat.f_bsize * stat.f_bfree;
    m_used = m_total - m_free;
    return true;
}

/******************************************************************************
 * EPOLL事件标志:
 * EPOLLIN : 输入事件,有数据待读取
 * EPOLLOUT: 输出事件,有数据要写入
 * EPOLLET : 边沿触发(事件就绪时,假设对事件没做处理,内核不会反复通知事件就绪)
 *           默认为水平触发(事件就绪时,假设对事件没做处理,内核会反复通知事件就绪)
 *****************************************************************************/

Poller::Poller() {}

Poller::~Poller() { close(); }

bool Poller::open(int maxEvents, bool onceNotify) {
    if (m_epfd > 0) {
        return true;
    }
    m_epfd = epoll_create1(0);
    if (m_epfd < 0) {
        return false;
    }
    m_events =
        (struct epoll_event*)calloc(maxEvents, sizeof(struct epoll_event));
    if (!m_events) {
        return false;
    }
    m_size = maxEvents;
    m_onceNotify = onceNotify;
    return true;
}
void Poller::close() {
    if (m_epfd > 0) {
        ::close(m_epfd);
        m_epfd = -1;
    }
    if (m_events) {
        free(m_events);
        m_events = nullptr;
    }
}
bool Poller::addEvent(const PollEvent& pe) {
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_epfd < 0 || m_eventNum >= m_size || pe.fd() < 0) {
        return false;
    }
    struct epoll_event evt;
    evt.data.fd = pe.fd();
    switch (pe.event()) {
        case PollEvent::POLLIN:
            evt.events = EPOLLIN;
            break;
        case PollEvent::POLLOUT:
            evt.events = EPOLLOUT;
            break;
        default:
            return false;
    }
    if (m_onceNotify) {
        evt.events |= EPOLLET;
    }
    if (epoll_ctl(m_epfd, EPOLL_CTL_ADD, evt.data.fd, &evt) < 0) {
        return false;
    }
    m_eventNum++;
    return true;
}

bool Poller::removeEvent(const PollEvent& pe) {
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_epfd < 0 || m_eventNum == 0) {
        return false;
    }
    struct epoll_event evt;
    evt.data.fd = pe.fd();
    switch (pe.event()) {
        case PollEvent::POLLIN:
            evt.events = EPOLLIN;
            break;
        case PollEvent::POLLOUT:
            evt.events = EPOLLOUT;
            break;
        default:
            return false;
    }
    if (m_onceNotify) {
        evt.events |= EPOLLET;
    }
    if (epoll_ctl(m_epfd, EPOLL_CTL_DEL, evt.data.fd, &evt) < 0) {
        return false;
    }
    m_eventNum--;
    return true;
}

std::vector<std::shared_ptr<PollEvent>> Poller::waitEvent(int usTimeout) {
    int msTimeout = usTimeout / 1000;
    std::vector<std::shared_ptr<PollEvent>> peVect;
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_epfd < 0) {
        return peVect;
    }

    int fds = epoll_wait(m_epfd, m_events, m_size, msTimeout);
    if (fds <= 0) {
        return peVect;
    }

    for (auto i = 0; i < fds; i++) {
        int fd = m_events[i].data.fd;
        std::shared_ptr<PollEvent> pe = nullptr;
        if (m_events[i].events & EPOLLIN) {
            pe = std::make_shared<PollEvent>(fd, PollEvent::POLLIN);
        } else if (m_events[i].events & EPOLLOUT) {
            pe = std::make_shared<PollEvent>(fd, PollEvent::POLLOUT);
        }
        if (pe) {
            peVect.emplace_back(std::move(pe));
        }
    }
    return peVect;
}
}  // namespace appkit
